package io.demo.petstore.account;

import com.google.common.collect.ImmutableList;
import io.demo.petstore.customer.Customer;
import io.demo.petstore.customer.CustomerDao;
import java.util.List;
import java.util.Optional;
import javax.annotation.PostConstruct;
import javax.validation.constraints.NotNull;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.stereotype.Service;

/**
 * @author lijinting01
 */
@Service
public class AccountServiceImpl implements AccountService, InitializingBean {

  private List<AccountValidator> registerAccountValidators;

  private final AccountDao accountDao;

  private final CustomerDao customerDao;

  @Autowired
  public AccountServiceImpl(AccountDao accountDao, CustomerDao customerDao) {
    this.accountDao = accountDao;
    this.customerDao = customerDao;
  }

  @Override
  public Account register(@NotNull Account account) {
    ensureValid(account);

    Customer customer = account.getCustomer();
    Example<Customer> query = Example
        .of(customer, ExampleMatcher.matching().withIgnoreCase()); // Ignore case matching last name

    // if there is any matching customer, find out and update this account.
    Optional.ofNullable(customerDao.findOne(query))
        .ifPresent(account::setCustomer);

    return accountDao.save(account);
  }

  private void ensureValid(Account account) {
    // 不用组合Predicate，因为每个Predicate要抛出的异常各不相同。
    registerAccountValidators.forEach(accountValidator -> {
      if (!accountValidator.test(account)) {
        throw accountValidator.accountException();
      }
    });
  }


  @Override
  @PostConstruct
  public void afterPropertiesSet() {
    registerAccountValidators = ImmutableList.of(
        new UsernameValidator(), // 0
        new PasswordValidator(), // 1
        new CustomerValidator(), // 2
        new UserExistValidator(accountDao) // 3 要查数据库（走IO）的放在最后
    );
  }
}